//
// Created by Administrator on 2020/10/14.
//

#include <getopt.h>
#include <sys/stat.h>
#include "../include/ueApi.h"
#include "../../ccore/misc/include/misc.h"
#include "../l1/include/l1Api.h"
#include "../l2/include/l2Api.h"
#include "../l3/include/l3Api.h"
#include "../nas/include/nasApi.h"

S_UeContext* gpUeContext = NULL;

int32_t UE_LoadUeInstConfig(toml_table_t* pUeInstTable, S_UeInstContext *context)
{
    int32_t rc;
    uint32_t cellLoop;
    uint32_t cellNum;
    char *ipStr;

    /* active */
    TOML_GET_BOOL_VALUE(pUeInstTable, "active", context->inUse);
    CSP_LOG_INF(UE_MD_COMM, "active : %u\n", context->inUse);

    return O5G_RC_OK;
}

int32_t UE_LoadConfig()
{
    int32_t rc;
    uint32_t ueInstLoop;
    uint32_t ueInstNum;
    struct stat buf;
    char tomlErrBuffer[128];

    if (lstat(gpUeContext->configFilePath, &buf) < 0)
    {
        CSP_LOG_ERR(UE_MD_COMM, "UE_LoadConfig() configFile[%s] error!\n", (char *)gpUeContext->configFilePath);
        return O5G_RC_ER;
    }
    if (!S_ISREG(buf.st_mode))
    {
        CSP_LOG_ERR(UE_MD_COMM, "UE_LoadConfig() configFileMode[%s] error!\n", (char *)gpUeContext->configFilePath);
        return O5G_RC_ER;
    }

    FILE* fp = fopen(gpUeContext->configFilePath, "rb");
    IF_RETURN(==, NULL, fp, O5G_RC_ER, "UE_LoadConfig() fopen() error!\n");

    toml_table_t* pTomlTableRoot;
    pTomlTableRoot = toml_parse_file(fp, tomlErrBuffer, sizeof(tomlErrBuffer));
    IF_RETURN(==, NULL, pTomlTableRoot, O5G_RC_ER, "UE_LoadConfig() toml_parse_file() error!\n");

    toml_array_t* pUeInstTableArray;
    pUeInstTableArray = toml_array_in(pTomlTableRoot, "UE_INST_List");
    IF_RETURN(==, NULL, pUeInstTableArray, O5G_RC_ER, "UE_LoadConfig() toml_parse_file() error!\n");

    ueInstNum = toml_array_nelem(pUeInstTableArray);
    IF_RETURN(<, UE_UE_INST_NUM_MAX, ueInstNum, O5G_RC_ER, "UE_LoadConfig() ueInstNum[%d] error!\n", ueInstNum);

    gpUeContext->ueInstNum = ueInstNum;
    for (ueInstLoop = 0; ueInstLoop < ueInstNum; ueInstLoop++)
    {
        toml_table_t* pUeInstTable;
        pUeInstTable = toml_table_at(pUeInstTableArray, ueInstLoop);
        IF_RETURN(==, NULL, pUeInstTable, O5G_RC_ER, "UE_LoadConfig() toml_table_at() error!\n");

        rc = UE_LoadUeInstConfig(pUeInstTable, &gpUeContext->ueInstContext[ueInstLoop]);
        IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_LoadConfig() UE_LoadUeInstConfig() error!\n");
    }

    toml_free(pTomlTableRoot);
    fclose(fp);

    return O5G_RC_OK;
}

int32_t UE_ParseCmdLineOpt(int argc, char* argv[])
{
    int32_t opt;
    int32_t index;

    optind  = 0;
    struct option options[] =
    {
        { "app-type", required_argument, NULL, 't' },
        { "config", required_argument, NULL, 'c' },
        { 0, 0, 0, 0 },
    };

    while (1)
    {
        index = 0;
        opt = getopt_long(argc, argv, "", options, &index);
        IF_BREAK(==, -1, opt, NO_LOG_MESSAGE);
        switch (opt)
        {
        case 't':
            gpUeContext->appType = O5G_GetAppType(optarg);
            if (APP_TYPE_UE != gpUeContext->appType)
            {
                CSP_LOG_ERR(UE_MD_COMM, "UE_ParseCmdLineOpt() appType[%d] error!\n", gpUeContext->appType);
                return O5G_RC_ER;
            }
            CSP_LOG_INF(UE_MD_COMM, "appType : %s\n", gAppTypeStrTable[gpUeContext->appType]);
            break;
        case 'c':
            IF_RETURN(==, NULL, optarg, O5G_RC_ER, "UE_ParseCmdLineOpt() config file path error!\n");
            gpUeContext->configFilePath = strdup(optarg);
            IF_RETURN(==, NULL, gpUeContext->configFilePath, O5G_RC_ER, "UE_ParseCmdLineOpt() strdup() error!\n");
            CSP_LOG_INF(UE_MD_COMM, "configFilePath : %s\n", gpUeContext->configFilePath);
            break;
        default:
            CSP_LOG_ERR(UE_MD_COMM, "UE_ParseCmdLineOpt() argv error!\n");
            return O5G_RC_ER;
        }
    }

    return O5G_RC_OK;
}

int32_t UE_Init(int argc, char* argv[])
{
    int32_t rc;
    uint32_t ueLoop;
    S_UeInstContext *pUeInstContext;

    rc = LOG_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() LOG_Init() error!\n");

    LOG_SetAppType("UE");
    LOG_SetModuleNameTable(gUeModuleNameTable, UE_MD_BUTT, UE_MODULE_NAME_PREFIX_LEN);

    rc = UE_MP_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_MP_Init() error!\n");

    gpUeContext = OS_MemCalloc(1, sizeof(S_UeContext));
    IF_RETURN(==, NULL, gpUeContext, O5G_RC_ER, "UE_Init() OS_MemCalloc() error!\n");

    rc = UE_ParseCmdLineOpt(argc, argv);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_ParseCmdLineOpt() error!\n");

    rc = UE_LoadConfig();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_LoadConfig() error!\n");

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInstContext = &gpUeContext->ueInstContext[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInstContext->inUse, NO_LOG_MESSAGE);

        pUeInstContext->pL2L1CpMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);
        IF_RETURN(==, NULL, pUeInstContext->pL2L1CpMsgQue, O5G_RC_ER, "UE_Init() VarRB_Create() error!\n");
        pUeInstContext->pL1L2CpMsgQue = VarRB_Create(NULL, 2 * 1024 * 1024, VAR_RB_TYPE_MPSC);
        IF_RETURN(==, NULL, pUeInstContext->pL1L2CpMsgQue, O5G_RC_ER, "UE_Init() VarRB_Create() error!\n");

        pUeInstContext->pL2L1UpMsgQue = VarRB_Create(NULL, 8 * 1024 * 1024, VAR_RB_TYPE_MPSC);
        IF_RETURN(==, NULL, pUeInstContext->pL2L1CpMsgQue, O5G_RC_ER, "UE_Init() VarRB_Create() error!\n");
        pUeInstContext->pL1L2UpMsgQue = VarRB_Create(NULL, 8 * 1024 * 1024, VAR_RB_TYPE_MPSC);
        IF_RETURN(==, NULL, pUeInstContext->pL1L2CpMsgQue, O5G_RC_ER, "UE_Init() VarRB_Create() error!\n");
    }

    rc = UE_L1_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_L1_Init() error!\n");

    rc = UE_L2_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_L2_Init() error!\n");

    rc = UE_L3_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_L3_Init() error!\n");

    rc = UE_NAS_Init();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Init() UE_NAS_Init() error!\n");

    CSP_LOG_INF(UE_MD_COMM, "UE_Init() success.\n");

    return O5G_RC_OK;
}

int32_t UE_Final(void)
{
    int32_t rc;
    uint32_t ueLoop;
    S_UeInstContext *pUeInstContext;

    rc = UE_L1_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() UE_L1_Final() error!\n");

    rc = UE_L2_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() UE_L2_Final() error!\n");

    rc = UE_L3_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() UE_L3_Final() error!\n");

    rc = UE_NAS_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() UE_NAS_Final() error!\n");

    for (ueLoop = 0; ueLoop < UE_UE_INST_NUM_MAX; ueLoop++)
    {
        pUeInstContext = &gpUeContext->ueInstContext[ueLoop];
        IF_CONTINUE(==, FALSE, pUeInstContext->inUse, NO_LOG_MESSAGE);

        VarRB_Destroy(pUeInstContext->pL2L1CpMsgQue);
        VarRB_Destroy(pUeInstContext->pL1L2CpMsgQue);

        VarRB_Destroy(pUeInstContext->pL2L1UpMsgQue);
        VarRB_Destroy(pUeInstContext->pL1L2UpMsgQue);
    }

    free(gpUeContext->configFilePath);
    OS_MemFree(gpUeContext);

    rc = UE_MP_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() UE_MP_Final() error!\n");

    CSP_LOG_INF(UE_MD_COMM, "UE_Final() success.\n");

    rc = LOG_Final();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Final() LOG_Final() error!\n");

    return O5G_RC_OK;
}

int32_t UE_Start(void)
{
    int32_t rc;

    rc = UE_L1_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Start() UE_L1_Start() error!\n");

    rc = UE_L2_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Start() UE_L2_Start() error!\n");

    rc = UE_L3_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Start() UE_L3_Start() error!\n");

    rc = UE_NAS_Start();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Start() UE_NAS_Start() error!\n");

    CSP_LOG_INF(UE_MD_COMM, "UE_Start() success.\n");

    return O5G_RC_OK;
}

int32_t UE_Stop(void)
{
    int32_t rc;

    rc = UE_L1_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Stop() UE_L1_Stop() error!\n");

    rc = UE_L2_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Stop() UE_L2_Stop() error!\n");

    rc = UE_L3_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Stop() UE_L3_Stop() error!\n");

    rc = UE_NAS_Stop();
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "UE_Stop() UE_NAS_Stop() error!\n");

    CSP_LOG_INF(UE_MD_COMM, "UE_Stop() success.\n");

    return O5G_RC_OK;
}
